package com.mxw.commons.cqrs;

import org.springframework.context.ApplicationContext;
import org.springframework.core.GenericTypeResolver;

import java.util.HashMap;
import java.util.Map;

@SuppressWarnings("unchecked")
public class Bus {
  private final Map<Class<? extends ICommand>, ICommandHandler<?, ? extends ICommand>>
      commandHandlerMap = new HashMap<>();
  private final Map<Class<? extends IQuery<?>>, IQueryHandler<?, ? extends IQuery<?>>>
      queryHandlerMap = new HashMap<>();

  ICommandHandler<?, ? extends ICommand> getCommandHandler(Class<? extends ICommand> commandType) {
    return commandHandlerMap.get(commandType);
  }

  IQueryHandler<?, ? extends IQuery<?>> getQueryHandler(Class<? extends IQuery<?>> queryType) {
    return queryHandlerMap.get(queryType);
  }

  public <R, C extends ICommand> R execute(C command) {
    ICommandHandler<R, C> commandHandler =
        (ICommandHandler<R, C>) getCommandHandler(command.getClass());
    return commandHandler.handle(command);
  }

  public <R, Q extends IQuery<R>> R execute(Q query) {
    IQueryHandler<R, Q> queryHandler =
        (IQueryHandler<R, Q>) getQueryHandler((Class<? extends IQuery<?>>) query.getClass());
    return queryHandler.handle(query);
  }

  public Bus(ApplicationContext applicationContext) {
    // 将所有ICommandHandler接口的实现类填充到commandHandlerMap
    String[] names = applicationContext.getBeanNamesForType(ICommandHandler.class);
    for (String name : names) {
      ICommandHandler<?, ? extends ICommand> commandHandler =
          applicationContext.getBean(name, ICommandHandler.class);
      registerCommandHandler(commandHandler);
    }

    // 将所有IQueryHandler接口的实现类填充到queryHandlerMap
    names = applicationContext.getBeanNamesForType(IQueryHandler.class);
    for (String name : names) {
      IQueryHandler<?, ? extends IQuery<?>> queryHandler =
          applicationContext.getBean(name, IQueryHandler.class);
      registerQueryHandler(queryHandler);
    }
  }

  /** commandHandlerMap填充 */
  public void registerCommandHandler(ICommandHandler<?, ? extends ICommand> commandHandler) {
    Class<?>[] generics =
        GenericTypeResolver.resolveTypeArguments(commandHandler.getClass(), ICommandHandler.class);
    Class<? extends ICommand> commandType = (Class<? extends ICommand>) generics[1];
    commandHandlerMap.put(commandType, commandHandler);
  }

  /** queryHandlerMap填充 */
  public void registerQueryHandler(IQueryHandler<?, ? extends IQuery<?>> queryHandler) {
    Class<?>[] generics =
        GenericTypeResolver.resolveTypeArguments(queryHandler.getClass(), IQueryHandler.class);
    Class<? extends IQuery<?>> queryType = (Class<? extends IQuery<?>>) generics[1];
    queryHandlerMap.put(queryType, queryHandler);
  }
}
